package com.geeaks.mybatis.generator.util;

import com.geeaks.mybatis.generator.enums.EnumDbType;
import com.geeaks.mybatis.generator.model.DatabaseConfig;
import com.geeaks.mybatis.generator.model.UITableColumnVO;
import org.mybatis.generator.internal.util.ClassloaderUtility;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.*;
import java.util.*;

@SuppressWarnings("unchecked")
public class DbUtil {

	private static final Logger logger = LoggerFactory.getLogger(DbUtil.class);

	private static final int db_connection_timeouts_seconds = 1;

	private static Map<EnumDbType, Driver> drivers;

	static {
		drivers = new HashMap<>();
		List<String> driverJars = ConfigHelper.getAllJDBCDriverJarPaths();
		ClassLoader classloader = ClassloaderUtility.getCustomClassloader(driverJars);
		EnumDbType[] dbTypes = EnumDbType.values();
		for (EnumDbType dbType : dbTypes) {
			try {
				Class<Driver> clazz = (Class<Driver>) Class.forName(dbType.getDriverClass(), true, classloader);
				Driver driver = clazz.newInstance();
				logger.info("load driver class: {}", driver);
				drivers.put(dbType, driver);
			} catch (Exception e) {
				logger.error("load driver error");
			}
		}
	}

	public static Connection getConnection(DatabaseConfig config) throws ClassNotFoundException, SQLException {
		String url = getConnectionUrlWithSchema(config);
		Properties props = new Properties();
		props.setProperty("user", config.getUsername()); //$NON-NLS-1$
		props.setProperty("password", config.getPassword()); //$NON-NLS-1$
		DriverManager.setLoginTimeout(db_connection_timeouts_seconds);
		Connection connection = drivers.get(EnumDbType.valueOf(config.getDbType())).connect(url, props);
		logger.info("getConnection, connection url: {}", connection);
		return connection;
	}

	public static List<String> getTableNames(DatabaseConfig config) throws Exception {
		String url = getConnectionUrlWithSchema(config);
		logger.info("getTableNames, connection url: {}", url);
		Connection connection = getConnection(config);
		try {
			List<String> tables = new ArrayList<>();
			DatabaseMetaData md = connection.getMetaData();
			ResultSet rs;
			if (EnumDbType.valueOf(config.getDbType()) == EnumDbType.SQL_Server) {
				String sql = "select name from sysobjects where xtype='u' or xtype='v' ";
				rs = connection.createStatement().executeQuery(sql);
				while (rs.next()) {
					tables.add(rs.getString("name"));
				}
			} else if (EnumDbType.valueOf(config.getDbType()) == EnumDbType.Oracle) {
				rs = md.getTables(null, config.getUsername().toUpperCase(), null, new String[] { "TABLE", "VIEW" });
			} else if (EnumDbType.valueOf(config.getDbType()) == EnumDbType.Sqlite) {
				String sql = "Select name from sqlite_master;";
				rs = connection.createStatement().executeQuery(sql);
				while (rs.next()) {
					tables.add(rs.getString("name"));
				}
			} else {
				// rs = md.getTables(null, config.getUsername().toUpperCase(), null, null);
				// 针对 postgresql 的左侧数据表显示
				rs = md.getTables(config.getSchema(), null, "%", new String[] { "TABLE", "VIEW" });
			}
			while (rs.next()) {
				tables.add(rs.getString(3));
			}
			return tables;
		} finally {
			connection.close();
		}
	}

	public static List<UITableColumnVO> getTableColumns(DatabaseConfig dbConfig, String tableName) throws Exception {
		String url = getConnectionUrlWithSchema(dbConfig);
		logger.info("getTableColumns, connection url: {}", url);
		Connection conn = getConnection(dbConfig);
		try {
			DatabaseMetaData md = conn.getMetaData();
			ResultSet rs = md.getColumns(null, null, tableName, null);
			List<UITableColumnVO> columns = new ArrayList<>();
			while (rs.next()) {
				UITableColumnVO columnVO = new UITableColumnVO();
				String columnName = rs.getString("COLUMN_NAME");
				columnVO.setColumnName(columnName);
				columnVO.setJdbcType(rs.getString("TYPE_NAME"));
				columns.add(columnVO);
			}
			return columns;
		} finally {
			conn.close();
		}
	}

	public static String getConnectionUrlWithSchema(DatabaseConfig dbConfig) throws ClassNotFoundException {
		EnumDbType dbType = EnumDbType.valueOf(dbConfig.getDbType());
		String connectionUrl = String.format(dbType.getConnectionUrlPattern(), dbConfig.getHost(), dbConfig.getPort(), dbConfig.getSchema(),
				dbConfig.getEncoding());
		logger.info("getConnectionUrlWithSchema, connection url: {}", connectionUrl);
		return connectionUrl;
	}
}
